﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace eslib.nnp5.RealTime
{
    /// <summary>
    /// 实时性队列发送器
    /// 保证传输可靠性前提（ack应答）
    /// 忽略中间的数据包，尽可能保证最后发送的数据包实时性
    /// </summary>
    public class RTQueueSender
    {
        AppUserBase app;

        /// <summary>
        /// ack模式返回true
        /// </summary>
        bool IsAckMode => app.senderMode == SenderMode.AckMode;


        /// <summary>
        /// 
        /// </summary>
        /// <param name="app"></param>
        /// <param name="rtChannelCount">实时通道数</param>
        public RTQueueSender(AppUserBase app, int rtChannelCount)
        {
            this.app = app;

            if (IsAckMode)
                app.transport.NewACKEvent += Transport_NewACKEvent;

            channels = new RTChannel[rtChannelCount];
            for (int i = 0; i < rtChannelCount; i++) channels[i] = new RTChannel();
        }


        //传输层ack事件
        private void Transport_NewACKEvent(ACKPkg ack)
        {
            trySend();
        }

        /// <summary>
        /// 尝试向app队列传送数据包
        /// </summary>
        private void trySend()
        {
            if (!app.QueueHasPkg)       //发送队列为空
            {
                appSend();
            }
            //发送队列非空不处理
        }

        /// <summary>
        /// 直接发送方法
        /// </summary>
        private void appSend()
        {
            var pkg = GetNextPkg();     //无数据返回null
            if (pkg != null)
            {
                app.SendPkg(pkg);
            }
        }



        /// <summary>
        /// 送入队列,会覆盖未发送的数据包,保证最后一个包的实时性
        /// 如果app为无ack模式时，直接发送
        /// 通道索引不正确时抛出异常
        /// </summary>
        /// <param name="pkg"></param>
        /// <param name="channelIndex">通道索引,从0开始</param>
        /// <exception cref="eslib.nnp5.NNP5Exception"></exception>
        public void SendPkg(IPackage pkg, int channelIndex)
        {
            if (channelIndex < 0 || channelIndex >= channelCnt) throw new NNP5Exception("索引不正确");
            channels[channelIndex].Package = pkg;

            if (IsAckMode)
            {                //尝试发送
                trySend();
            }
            else        //无ack模式，直接发送
            {
                appSend();
            }
        }



        #region 通道处理

        RTChannel[] channels;
        int channelCnt => channels.Length;

        //下一次循环的通道索引指针
        int channelPtr = 0;

        /// <summary>
        /// 获取下一个发送的通道数据包，无数据返回null
        /// </summary>
        /// <returns></returns>
        IPackage GetNextPkg()
        {
            for (int i = 0; i < channelCnt; i++)    //最多循环次数与通道数一致
            {
                var pkg = channels[channelPtr].Package;        //为空返回null

                channelPtr++;
                if (channelPtr >= channelCnt) channelPtr = 0;

                if (pkg != null)
                {
                    return pkg;
                }
            }
            return null;
        }

        #endregion

    }
}
